#ifndef _VFLOAT_H_
#define _VFLOAT_H_

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <string>
#include <cmath>
#include "Convert.hpp"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Types {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

#define round(X) ( ( (X) > 0.5 ) ? ceil(X) : floor(X) )

/**
 *  Variable size float.
 *	This class creates a variable memory size float.
 */
class VFloat {

	public:

		/// Creation
		VFloat(
			const bool is_signed = true,
			const unsigned int int_size = 5,
			const unsigned int dec_size = 10
		) : is_signed(is_signed), int_size(int_size), dec_size(dec_size)
		{
			this->value.resize(this->int_size+this->dec_size+(this->is_signed?1:0));
		}
		
		/// Creation with initialisation values
		VFloat(
			const bool is_signed = true,
			const unsigned int int_size = 5,
			const unsigned int dec_size = 10,
			const double value
		) : is_signed(is_signed), int_size(int_size), dec_size(dec_size)
		{
			this->value.resize(this->int_size+this->dec_size+(this->is_signed?1:0));
			this->SetStringFromFloat(value);
		}

		/// Create a copy of the given instance
		VFloat(const VFloat & inst)
		:	is_signed(inst.is_signed),
			int_size(inst.int_size),
			dec_size(inst.dec_size)
		{}
		
		/// Affectation operator
		VFloat & operator = (const VFloat & inst) {
			if (this != &inst) {
				this->is_signed	= inst.is_signed;
				this->int_size	= inst.int_size;
				this->dec_size	= inst.dec_size;
			}
			return *this;
		}
		
		/// Returns the sign
		bool & IsSigned() { return this->is_signed; }
		const bool & GetIsSigned() const { return this->is_signed; }
		
		/// Returns the integer part size
		unsigned int & IntSize() { return this->int_size; }
		const unsigned int GetIntSize() const { return this->int_size; }
		
		/// Returns the decimal part size
		unsigned int & DecSize() { return this->dec_size; }
		const unsigned int GetDecSize() const { return this->dec_size; }
		
		/// Returns the string value
		std::string & StringValue() { return this->value; }
		const std::string & GetStringValue() const { return this->value; }
		
		/// Returns the float value
		const double GetFloatValue() {
			using namespace Toolkit;
			double result = 1.f;
			result *= (double)BStrToUInt(this->value.substr(this->is_signed?1:0,this->int_size));
			result += ((double)BStrToUInt(this->value.substr((this->is_signed?1:0)+this->int_size,this->dec_size)))/(pow(2.f,this->dec_size));
			if ((this->is_signed) && (this->value[0] == '0'))
				result *= -1.f;
			return result;
		}
		
		/// Set string value from a float
		void SetStringFromFloat(const double v) {
			using namespace Toolkit;
			if (this->is_signed)
				this->value[0] = (v>=0)?'1':'0';
			unsigned int	int_part = (unsigned int)floor(fabs(v)),
							dec_part = (unsigned int)round((fabs(v)-floor(fabs(v)))*pow(2.f,this->dec_size));
			if (dec_part == pow(2.f,this->dec_size)) {
				++int_part;
				dec_part = 0;
			}
			this->value.replace(
				this->is_signed?1:0,
				this->int_size,
				UIntToBStr(int_part,this->int_size)
			);
			this->value.replace(
				(this->is_signed?1:0)+this->int_size,
				this->dec_size,
				UIntToBStr(dec_part,this->dec_size)
			);
		}
		
		/// Change the sign
		void ChangeSign() {
			if (this->is_signed)
				this->value[0] = (this->value[0]=='0')?'1':'0';
		}
		
		/// Returns true if the sign is positive
		const bool IsPositive() const { return !this->is_signed || (this->value[0]=='1'); }
		
	protected:
		
		/// Signed or not
		bool is_signed;
		
		/// Integer part size
		unsigned int int_size;
		
		/// Decimal part size
		unsigned int dec_size;
		
		/// Value
		std::string value;

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif
